package com.example.action;

import java.util.Objects;

import com.example.execute.BaseExecute;
import com.example.execute.Execute;
import com.example.execute.GoToImplExecute;
import com.example.handler.impl.DefaultGoToImplHandler;
import com.example.handler.impl.FeignMethodGoToImplHandler;
import com.example.handler.impl.MapperGoToImplHandler;
import com.example.util.CustomNotifier;
import com.example.util.PsiElementUtil;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiMethodCallExpression;
import com.intellij.psi.PsiReferenceExpression;
import com.intellij.psi.PsiTypeElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;

/**
 * @author Aaron
 * @since 2020/11/6 13:28
 * <p>描述：</p>
 */
public class GoToImplAction extends AnAction {

    @Override
    public void actionPerformed(AnActionEvent anActionEvent) {
        PsiElement currentPsiElement = anActionEvent.getData(LangDataKeys.PSI_ELEMENT);
        if (currentPsiElement == null) {
            CustomNotifier.error(anActionEvent.getProject(), "No elements are selected!");
            return;
        }

        // 根据本插件的目标，这里被选择的元素可以是以下几种情况：
        // 1 Feign 接口上的方法，跳转到本模块或其它模块的接口方法实现上
        // 2 Mapper 接口，跳转到对应的 xml 文件
        // 3 Mapper 接口上的方法，跳转到对应的 xml 文件的 sql 上

        Execute execute = new BaseExecute() {
        };
        if (PsiElementUtil.isFeignMethod(currentPsiElement)) {
            // Feign 方法跳转处理
            execute = new GoToImplExecute(new FeignMethodGoToImplHandler(anActionEvent, currentPsiElement));
        } else if (PsiElementUtil.isMapperInterface(currentPsiElement)) {
            // Mapper 接口跳转处理
            execute = new GoToImplExecute(new MapperGoToImplHandler(anActionEvent,
                    (PsiClass) currentPsiElement, null));
        } else if (PsiElementUtil.isMapperInterfaceField(currentPsiElement)) {
            // Mapper 对象变量跳转处理：获取其类型，然后转化为 PsiClass
            PsiTypeElement psiTypeElement = ((PsiField) currentPsiElement).getTypeElement();
            PsiClass psiClass = PsiUtil.resolveClassInType(Objects.requireNonNull(psiTypeElement).getType());
            execute = new GoToImplExecute(new MapperGoToImplHandler(anActionEvent, psiClass, null));
        } else if(PsiElementUtil.isBaseMapperMethod(currentPsiElement)) {
            // baseMapper 方法
            execute = new GoToImplExecute(new MapperGoToImplHandler(anActionEvent,
                getActualPsiElement(anActionEvent), (PsiMethod) currentPsiElement));
        }   else if (PsiElementUtil.isMapperMethod(currentPsiElement)) {
            // Mapper 接口方法跳转
            execute = new GoToImplExecute(new MapperGoToImplHandler(anActionEvent,
                    (PsiClass) currentPsiElement.getParent(), (PsiMethod) currentPsiElement));
        }else {
            // 其它情况处理
            execute = new GoToImplExecute(new DefaultGoToImplHandler(anActionEvent, currentPsiElement));
        }

        execute.doExecute();
    }

    private static PsiClass getActualPsiElement(AnActionEvent event) {
        // 获取当前编辑器
        Editor editor = event.getData(CommonDataKeys.EDITOR);
        if (editor == null) return null;

        // 获取当前文件
        PsiFile psiFile = event.getData(CommonDataKeys.PSI_FILE);
        if (psiFile == null) return null;

        // 获取光标位置的元素
        int offset = editor.getCaretModel().getOffset();
        PsiElement element = psiFile.findElementAt(offset);
        if (element == null) return null;

        // 获取方法引用表达式
        PsiMethodCallExpression methodCall = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
        if (methodCall == null) return null;

        // 获取方法引用
        PsiReferenceExpression methodExpression = methodCall.getMethodExpression();

        // 获取方法调用的限定符（即 waybillDAO 部分）
        PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
        if (qualifierExpression == null)
            // 没有时，返回当前类
            return PsiTreeUtil.getParentOfType(element, PsiClass.class);
        return PsiUtil.resolveClassInType(qualifierExpression.getType());
    }

}
